package api.action;

import api.model.ApiColumn;
import api.model.ApiTable;
import api.service.ApiTableService;
import m.common.action.ActionMeta;
import m.common.action.RequestMeta;
import m.common.action.VuePageResult;
import m.common.action.VueResult;
import m.common.model.Model;
import m.common.model.TableMeta;
import m.common.model.config.ModelConfig;
import m.common.model.util.*;
import m.html.VueCollectResult;
import m.html.VueFormResult;
import m.html.VueListResult;
import m.html.collect.CollectColumn;
import m.html.collect.CollectColumnType;
import m.html.element.Element;
import m.html.element.attr.Align;
import m.html.element.attr.ButtonType;
import m.html.element.other.DisplayItem;
import m.html.element.other.ItemsQuery;
import m.html.element.other.Param;
import m.html.form.*;
import m.html.list.*;
import m.system.cache.CacheUtil;
import m.system.db.TransactionManager;
import m.system.exception.MException;
import m.system.util.*;
import manage.action.ManageAction;
import manage.model.AdminLogin;
import manage.util.DictionaryUtil;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@ActionMeta(name="api/apiTable")
public class ApiTableAction extends ManageAction {
    private ApiTable model;

    /**
     * 列表页面 根据tableOid展示数据
     *
     *  Exception
     */
    public VueResult toTablePage() throws Exception {
        boolean isExport="Y".equals(getParamsValue("export"));//导出模板
        verifyMenu("A2101");
        verifyAdminOperPower("manage_api_power");
        AdminLogin admin=getSessionAdmin();
        String tableOid=getParamsValue("tableOid");
        ApiTable apiTable=CacheUtil.get(ApiTable.class,tableOid);
        List<ApiColumn> cols=CacheUtil.getList(ApiColumn.class,apiTable.getOid());
        FromQueryList from=FromQueryList.from("a",apiTable.getOid());
        if(isExport) from.addCondition("1=2");//导出模板，不查数据
        VueCollectResult result=getVueCollectResult(apiTable.getDescription(), from, actionUrl("toTablePage"));
        result.setHeight(-1);
        result.setFunction(true,false);
        for(int i=0;i<cols.size();i++){
            ApiColumn col=cols.get(i);
            if("Y".equals(col.getIsinit())){
                String fk="f"+i;
                String field="#{a."+col.getName()+"} "+fk;
                String text=col.getDescription().split("\\|")[0];
                if(isExport){
                    if(!StringUtil.isSpace(col.getDictType())){
                        text+="\r\n取值："+ ArrayUtil.connection(DictionaryUtil.getTitle(col.getDictType()).values().toArray(new String[0]),",");
                    }else if(!StringUtil.isSpace(col.getDictOption())){
                        text+="\r\n取值："+ ArrayUtil.connection(DisplayItem.titles(DisplayItem.to(col.getDictOption().split(":|,"))),",");
                    }else if(ObjectUtil.notEmptyOid(col.getModelTable())){
                        text+="\r\n取值："+col.getModelTable().getDescription()+"（"+col.getModelTable().getName()+"）-主键";
                    }
                }
                CollectColumn cc;
                if("DATE".equals(col.getType())){
                    cc=collectDateTimeColumn(text,field,140,true).setDateFormat(DateUtil.YYYY_MM_DD_HH_MM_SS);
                }else if("INT".equals(col.getType())){
                    cc=collectIntegerColumn(text,field,100,true);
                }else if("DOUBLE".equals(col.getType())){
                    cc=collectNumberColumn(text,field,120,true);
                }else{
                    int width=80+col.getLength();
                    if(width>350) width=350;
                    cc=collectTextColumn(text,field,width,true);
                }
                if(!StringUtil.isSpace(col.getDictType())){
                    cc.setDictType(col.getDictType());
                }else if(!StringUtil.isSpace(col.getDictOption())){
                    cc.setDisItems(DisplayItem.to(col.getDictOption().split(":|,")));
                }
                if(!StringUtil.isSpace(col.getModelClass())){
                    cc.setType(CollectColumnType.LINK)
                    .setLinkEvent(
                        new ListEvent(actionUrl("toViewModel"), EventType.MODAL,600)
                            .addParam(Param.value("modelClass",col.getModelClass()))
                            .addParam(fk+"1",Param.field("oid","#{a."+col.getName()+"}"))
                    );
                }else if(ObjectUtil.notEmptyOid(col.getModelTable())) {
                    cc.setType(CollectColumnType.LINK)
                    .setLinkEvent(
                        new ListEvent(actionUrl("toViewTable"), EventType.MODAL,600)
                            .addParam(Param.value("tableOid",col.getModelTable().getOid()))
                            .addParam(fk+"1",Param.field("oid","#{a."+col.getName()+"}"))
                    );
                }
                getParams().putIfAbsent("view_" + fk, "true");
                if(isExport) cc.setCount(ListCount.NONE);
                result.addCol(cc.setSort(true));
            }
        }
        if("N".equals(apiTable.getSource())){
            result.addButton(ListButton.modal("新增",ButtonType.PRIMARY,actionUrl("toEditModel"),600,SuccessMethod.REFRESH)
                .addQueryParam(Param.value("tableOid",apiTable.getOid())));
            result.addButton(ListButton.importExcel("导入",ButtonType.SUCCESS,actionUrl())
                .addQueryParam(Param.value("importParam",apiTable.getOid())));
            result.addCol(collectSpaceColumn("操作",
                ListButton.modal("修改",ButtonType.PRIMARY,actionUrl("toEditModel"),600,SuccessMethod.REFRESH)
                    .addQueryParam(Param.value("tableOid",apiTable.getOid()))
                    .addParam("oid","#{a.oid}"),
                ListButton.ajax("删除",ButtonType.ERROR,actionUrl("doDeleteModel"),SuccessMethod.REFRESH)
                    .addParam(Param.value("tableOid",apiTable.getOid()))
                    .addParam("oid","#{a.oid}")
                    .setConfirm("确定要删除吗？")
            ));
        }
        return result;
    }
    @RequestMeta(params = {"tableOid","oid"})
    public JSONMessage doDeleteModel(String tableOid,String oid) throws MException {
        TableUpdateUtil.deleteModel(tableOid,oid);
        return new JSONMessage(0,"删除成功");
    }
    @RequestMeta(params = {"tableOid","model"})
    public JSONMessage doSaveModel(String tableOid,Map<String,Object> model) throws Exception {
        systemLog("模型","保存"+tableOid+"信息");
        verifyAdminOperPower("manage_api_power");
        String msg;
        if (null == model.get("oid") || StringUtil.isSpace(model.get("oid").toString())) {
            //不存在oid属性
            model.put("oid", GenerateID.generatePrimaryKey());
            TableUpdateUtil.insertModel(tableOid, model);
            msg = "保存成功";
        } else if (!TableQueryList.getList(tableOid, new String[]{"oid"}, null, QueryCondition.eq("oid", model.get("oid"))).isEmpty()) {//oid在库
            TableUpdateUtil.updateModel(tableOid, model);
            msg = "修改成功";
        } else {//oid不在库
            model.put("oid", model.get("oid"));
            TableUpdateUtil.insertModel(tableOid, model);
            msg = "保存成功";
        }
        return new JSONMessage(0,msg)
                .push("model", model);
    }
    @RequestMeta(params = {"tableOid","oid"})
    public VueResult toEditModel(String tableOid,String oid) throws Exception {
        ApiTable apiTable=CacheUtil.get(ApiTable.class,tableOid);
        List<ApiColumn> cols=CacheUtil.getList(ApiColumn.class,apiTable.getOid());
        List<Map<String, Object>> ls = TableQueryList.getList(tableOid, new String[]{"*"}, null,
                QueryCondition.eq("oid", oid));
        JSONMessage model=ls.isEmpty()?new JSONMessage():new JSONMessage(ls.get(0));

        ModelForm form = new ModelForm(apiTable.getDescription(),1);
        for(int i=0;i<cols.size();i++) {
            ApiColumn col = cols.get(i);
            if ("Y".equals(col.getIsinit())) {
                String field="model."+col.getName();
                String text=col.getDescription().split("\\|")[0];
                if("DATE".equals(col.getType())){
                    form.addItem(FormInput.dateTime(text,field).setPlaceholder("请选择"+text).setRequired("Y".equals(col.getNotnull())));
                }else if("INT".equals(col.getType())){
                    form.addItem(FormInput.number(text,field,"#",0).setPlaceholder("请输入"+text).setRequired("Y".equals(col.getNotnull())));
                }else if("DOUBLE".equals(col.getType())){
                    form.addItem(FormInput.number(text,field).setPlaceholder("请输入"+text).setRequired("Y".equals(col.getNotnull())));
                }else if(ObjectUtil.notEmptyOid(col.getModelTable())){
                    if(null==model.get(col.getName())) model.push(col.getName(),new JSONMessage());
                    form.addItem(FormInput.selectJsPage(text,field,"oid","'"+actionUrl(ApiInterAction.class,"selectTablePage")+"?isOpen=true&params.tableOid="+col.getModelTable().getOid()+"'",1000,"{{"+field+".oid}}").setRequired("Y".equals(col.getNotnull())));
                }else{
                    String dict=StringUtil.noSpace(col.getDictType(),col.getDictOption());
                    if(StringUtil.notSpace(dict)){
                        form.addItem(FormInput.select(text,field,dict).setPlaceholder("请选择"+text).setRequired("Y".equals(col.getNotnull())));
                    }else{
                        form.addItem(FormInput.input(text,field).setPlaceholder("请输入"+text).setRequired("Y".equals(col.getNotnull())));
                    }
                }
            }
        }
        form.addData("model",model)
        .addButton(
            FormButton.pri("保存","doSaveModel")
        )
        .addMethod(
            FormEvent.backFlag,
            FormEvent.doJson("doSaveModel",actionUrl("doSaveModel"),"{tableOid:'"+tableOid+"',model:this.model}")
                .success("backFlag")
        );
        VueFormResult result=new VueFormResult();
        return result.addForm(form)
        .addForm(
            new CustomForm("提示",false)
            .addElement(
                Element.tag("a-alert").A("message","提示").A("show-icon")
                .E(
                    Element.tag("template").A("#description")
                    .E(
                        Element.tag("div").E("主键为空：保存时会生成新的主键并插入数据。"),
                        Element.tag("div").E("主键不为空：存在录入的主键时，会更新已存在的数据，不存在则会插入新的数据。")
                    )
                )
            )
        );
    }

    @Override
    protected JSONMessage processImportSetting(String importParam) {
        ApiTable apiTable=CacheUtil.get(ApiTable.class,importParam);
        return new JSONMessage()
            .push("tempName",apiTable.getDescription())
            .push("tempPath",actionUrl("exportExcel")+"?method=toTablePage&params[export]=Y&params[tableOid]="+apiTable.getOid())
            .push("alertMessage","导入数据根据主键控制唯一，导入重复的主键则覆盖；主键列为空则自动生成唯一主键。");
    }
    @Override
    protected String processImportData(List<String[]> data, String importParam) throws Exception {
        boolean power=hasAdminOperPower("manage_api_power");
        if(!power) throw new MException(this.getClass(),"当前账号无权导入数据");
        ApiTable apiTable=CacheUtil.get(ApiTable.class,importParam);
        List<ApiColumn> cols=CacheUtil.getList(ApiColumn.class,apiTable.getOid());
        ApiTableService service=getService(ApiTableService.class);
        setImportCurrentTitle("正在处理【"+apiTable.getDescription()+"】数据");
        String msg;
        TransactionManager tm=new TransactionManager();
        try{
            tm.begin();
            int n=0,m=0;
            StringBuilder error=new StringBuilder();
            for(int i=0;i<data.size();i++){
                String[] obj=data.get(i);
                obj=parseRowData(obj,cols.size());
                int row=i+1;
                StringBuffer e=new StringBuffer();
                Map<String,Object> d=new HashMap<>();
                for(int j=0;j<cols.size();j++){
                    ApiColumn ac=cols.get(j);
                    String name=ac.getDescription().split("\\|")[0];
                    if("DOUBLE".equals(ac.getType())){
                        d.put(ac.getName(),parseDouble(obj[j],row,name,e));
                    }else if("INT".equals(ac.getType())){
                        d.put(ac.getName(),parseInteger(obj[j],row,name,e));
                    }else if("DATE".equals(ac.getType())){
                        d.put(ac.getName(),parseDate(obj[j],row,name,e));
                    }else{
                        d.put(ac.getName(),parseString(obj[j]));
                    }
                    if(null==d.get(ac.getName())&&"Y".equals(ac.getNotnull())&&!"oid".equals(ac.getName())){
                        e.append("\r\n第").append(row).append("行").append(name).append("不能为空");
                    }
                    if("STRING".equals(ac.getType())&&StringUtil.notSpace((String)d.get(ac.getName()))){
                        String v=(String)d.get(ac.getName());
                        if(StringUtil.notSpace(ac.getDictType())){
                            if(null==DictionaryUtil.getValue(ac.getDictType(), v))
                                e.append("\r\n第").append(row).append("行").append(name).append("取值错误");
                        }else if(StringUtil.notSpace(ac.getDictOption())){
                            List<DisplayItem> ls=DisplayItem.to(ac.getDictOption().split(":|,"));
                            if(null==DisplayItem.getValue(ls,v))
                                e.append("\r\n第").append(row).append("行").append(name).append("取值错误");
                        }else if(ac.getLength()<v.length()){
                            e.append("\r\n第").append(row).append("行").append(name).append("长度不能超过").append(ac.getLength());
                        }
                    }else if(("MODEL".equals(ac.getType())||"TABLE".equals(ac.getType()))
                            &&StringUtil.notSpace((String)d.get(ac.getName()))){
                        String v=(String)d.get(ac.getName());
                        SystemUtil.println(ac);
                        List<Map<String,Object>> ml= TableQueryList.getList(ac.getModelTable().getOid(),new String[]{"oid"},null,
                                QueryCondition.eq("oid",v));
                        if(ml.isEmpty()){
                            e.append("\r\n第").append(row).append("行").append(name).append("取值错误");
                        }
                    }
                }
                if(e.length()>0){
                    error.append(e);
                }else {
                    boolean b=service.saveModel(apiTable.getOid(), d);
                    if(!b) m++;
                }
                n++;
            }
            if(error.length()>0){
                throw new MException(this.getClass(),error.toString());
            }
            msg="成功导入了"+n+"条！"+(m>0?("其中"+m+"条是更新"):"");
            tm.commit();
        }catch (Exception e){
            tm.rollback();
            throw e;
        }
        return msg;
    }


    @RequestMeta(params = {"modelClass","oid"})
    public VueResult toViewModel(String modelClass,String oid) throws Exception {
        verifyAdminOperPower("manage_api_power");
        Model mc = ClassUtil.newInstance(modelClass);
        mc.setOid(oid);
        return new VuePageResult("page/base/api/viewModel.js")
            .addParam("json", ModelQueryUtil.getModel(mc));
    }
    @RequestMeta(params = {"tableOid","oid"})
    public VueResult toViewTable(String tableOid,String oid) throws Exception {
        verifyAdminOperPower("manage_api_power");
        List<Map<String, Object>> ls = TableQueryList.getList(tableOid, new String[]{"*"}, null,
            QueryCondition.eq("oid", oid));
        return new VuePageResult("page/base/api/viewModel.js")
            .addParam("json", !ls.isEmpty() ?ls.get(0):new JSONMessage());
    }

    public JSONMessage doSave() throws Exception {
        systemLog("模型","保存模型信息");
        verifyAdminOperPower("manage_api_power");
        String msg=getService(ApiTableService.class).save(model);
        return new JSONMessage(0,msg)
                .push("model", model);
    }
    public JSONMessage doDelete() throws Exception {
        systemLog("模型","删除模型信息");
        verifyAdminOperPower("manage_api_power");
        String msg=getService(ApiTableService.class).delete(model);
        return new JSONMessage(0,msg);
    }
    public JSONMessage doInit() throws Exception {
        systemLog("模型","初始化模型信息");
        verifyAdminOperPower("manage_api_power");
        String msg=getService(ApiTableService.class).init(model);
        CacheUtil.clear(ApiColumn.class);
        CacheUtil.clear(ApiTable.class);
        return new JSONMessage(0,msg);
    }
    public JSONMessage getSourceClassOptions(){
        List<DisplayItem> sourceList= new ArrayList<>();
        List<Class<? extends Model>> classList=ModelConfig.getTableList();
        for(Class<? extends Model> clazz : classList){
            TableMeta tm=ModelConfig.getTableMeta(clazz);
            boolean isSys= clazz.getName().contains("manage.model") || clazz.getName().contains("flow.model")
                    || clazz.getName().contains("api.model");
            DisplayItem data=DisplayItem.value(clazz.getName(),(isSys?"[系统] ":"")+tm.description()+" "+tm.name());
            sourceList.add(data);
        }
        return DictionaryUtil.toDictData(sourceList);
    }
    public JSONMessage getApiTablesOptions() throws Exception {
        return DictionaryUtil.toDictData(
            new ItemsQuery(ApiTable.class,"name","oid")
                .setTitleExpression("concat(#{name},' - ',ifnull(#{description},''))")
                .addCond(QueryCondition.eq("isinit","Y"))
                .toItems()
        );
    }
    public VueResult toEdit() throws Exception {
        verifyMenu("A2101");
        verifyAdminOperPower("manage_api_power");
        if(null!=model&&!StringUtil.isSpace(model.getOid())){
            model= ModelQueryList.getModel(model,"*");
        }else {
            model=new ApiTable();
            model.setSource("N");
        }
        ModelForm form = new ModelForm("数据模型");
        form.addItem(
            FormTest.test("model.oid", FormHtml.ins("来源",
                Element.tag("a-space").E(Element.tag("a-tag").A("color","processing")
                .E("{{dict('api.model.ApiTable!source',model.source)}}"))
                .E(Element.tag("span").A("v-if","model.source=='Y'").E(" {{model.sourceDesc}} {{model.sourceName}}"))
                .E(Element.tag("span").A("v-else").E(" {{model.oid}}"))
            ).setSpan(3)),
            FormTest.test("!model.oid",FormInput.radio("来源", "model.source", "api.model.ApiTable!source").setSpan(3)),
            FormTest.test("!model.oid&&model.source=='Y'",FormInput.selectItem("来源表","model.sourceClass",actionUrl("getSourceClassOptions")).setSpan(3)),
            FormInput.input("{{model.source=='B'?'表名':'标识'}}","model.name").setPlaceholder("请输入").setDescription("只能输入小写字母数字和下划线"),
            FormInput.input("描述","model.description").setPlaceholder("请输入描述").setSpan(2),
            FormTest.test("model.source=='A'",FormInput.input("视图SQL","model.sourceSql").setPlaceholder("请输入视图SQL").setSpan(3))
        )
        .addData("model",model)
        .addData("sourceCols",new ArrayList<>())
        .addWatch("model.sourceClass","this.getSourceCols();")
        .setMounted("if(this.model.sourceClass) this.getSourceCols();")
        .addButton(
            FormButton.pri("保存","doSave")
        )
        .addMethod(
            FormEvent.backFlag,
            FormEvent.doJson("doSave",actionUrl("doSave"),"{model:this.model}")
                .fillData("model").success("loadColumnPage"),
            FormEvent.doJson("getSourceCols",actionUrl(ApiColumnAction.class,"getTableMetaColumn"),"{tableClass:this.model.sourceClass}")
                .fillData("sourceCols")
        );
        CustomForm customForm=new CustomForm("列信息").setTest("model.source=='Y'")
            .addElement(
                Element.tag("a-list").A("size","small").A(":data-source","sourceCols")
                .E(Element.tag("template").A("#renderItem","{ item }")
                    .E(Element.tag("a-list-item")
                        .E(
                            Element.tag("div").A("style","flex:3;").E("{{item.name}}"),
                            Element.tag("div").A("style","flex:4;").E("{{item.desc}}"),
                            Element.tag("template").A("v-if","item.type")
                            .E(
                                Element.tag("div").A("style","flex:2;").E("{{item.type}}"),
                                Element.tag("div").A("style","flex:1;").E("{{item.length}}"),
                                Element.tag("div").A("style","flex:1;").E("{{item.defaultValue}}"),
                                Element.tag("div").A("style","flex:1;").E("{{item.notnull}}")
                            ),
                            Element.tag("template").A("v-else")
                            .E(
                                Element.tag("div").A("style","flex:4;").E("{{item.class}}"),
                                Element.tag("div").A("style","flex:1;").E("{{item.notnull}}")
                            )
                        )
                    )
                )
            );
        LoadPage columnPage=new LoadPage("loadColumnPage",
            actionUrl(ApiColumnAction.class,"apiColumnList"),"{params:{tableOid:this.model.oid,oper:'edit'}}")
                .setTest("model.source!='Y'").setMounted("if(this.model.source!='Y')this.loadColumnPage();");
        VueFormResult result=new VueFormResult();
        return result.addForm(form).addForm(customForm).addForm(columnPage);
    }
    public VueResult apiTableList() throws Exception{
        verifyMenu("A2101");
        boolean power=hasAdminOperPower("manage_api_power");
        VueListResult result=getVueListResult("数据表列表", ApiTable.class,actionUrl("apiTableList"));
        result.setSubTitle("添加或者编辑数据表信息。");
        result.addCol(toIndexColumn());
        result.addCol(toTextColumn("标识", "name",true));
        result.addCol(toTextColumn("描述", "description",200,true));
        result.addCol(toTextColumn("来源", "source",true).setDictOption(ApiTable.class,"source"));
        result.addCol(toTextColumn("来源表", "sourceDesc",true));
        result.addCol(toTextColumn("生成", "isinit",true).addDisItem("Y","是","N","否"));
        if(power) result.addCol(toSpaceColumn("操作",200,
            ListButton.modal("修改", ButtonType.PRIMARY,actionUrl("toEdit"),900, SuccessMethod.REFRESH)
                .addParam("model.oid","oid"),
            ListElement.test("!(record.source=='Y'||record.source=='B')",
                ListButton.ajax("生成", ButtonType.SUCCESS,actionUrl("doInit"), SuccessMethod.REFRESH)
                    .addParam("model.oid","oid").setConfirm("生成表和字段，可重复操作，生成后字段不可修改。")
            ),
            ListElement.test("record.isinit!='Y'",
                ListButton.ajax("删除", ButtonType.WARN,actionUrl("doDelete"), SuccessMethod.REFRESH)
                    .addParam("model.oid","oid").setConfirm("确定要删除该自建表吗?")
            ),
            ListElement.elseOther(
                ListButton.page("查询", ButtonType.LINK,actionUrl("toTablePage"), SuccessMethod.REFRESH)
                    .addParam("params[tableOid]","oid")
            )
        ).setFixed(Align.RIGHT));
        if(power) result.addButton(
            ListButton.modal("新增",ButtonType.PRIMARY,actionUrl("toEdit"),900,SuccessMethod.REFRESH)
        );
        return result;
    }

    public ApiTable getModel() {
        return model;
    }

    public void setModel(ApiTable model) {
        this.model = model;
    }

}
